#include "poolthread.h"
#include <vector>
#include <atomic>
#include <memory>
#include <map>

class CThreadPool{

public:
    CThreadPool(int maxThreadsNum=4,int minThreadsNum=2,int idelTimepout=30000,int maxAge=32):
        m_maxThreadsNum(maxThreadsNum),
        m_minThreadsNum(minThreadsNum),
        m_idelTimepout(idelTimepout),
        m_age(0),
        m_maxAge(maxAge)
    {
        std::unique_lock<mutex> lock(m_threadsMtx);
        for(int i =0;i< minThreadsNum;i++)
        {
            m_threads.emplace_back(std::make_shared<CPoolthread>());
        }
    }

    ~CThreadPool()
    {
        StopAll();
        std::unique_lock<mutex> lock(m_threadsMtx);
        m_threads.swap(std::vector<std::shared_ptr<CPoolthread>>());
    }

    template<typename F,typename... Args>
    void Start(F&& f,Args&&... args)
    {
        bool pend;
        auto ptrThread = GetThread(pend);
        if(pend)
        {
            ptrThread->AddPending(std::forward<F>(f),std::forward<Args>(args)...);
        }
        else
        {
            ptrThread->Start(std::forward<F>(f),std::forward<Args>(args)...);
        }        
    }

    void StopAll()
    {
        std::unique_lock<mutex> lock(m_threadsMtx);
        for(auto ptrThread:m_threads)
        {
            ptrThread->Stop();
            ptrThread->Join();
        }
    }

private:
    std::shared_ptr<CPoolthread> GetThreadPtr(bool& pending=false)
    {
        std::shared_ptr<CPoolthread> ptrPoolThread;
        std::shared_ptr<CPoolthread> minPendingNumThread;
        
        std::unique_lock<mutex> lock(m_threadsMtx);
        if(m_age++ >= m_maxAge)
        {
            ReleasIdelThreads();
        }
        int minPendingNum = -1;
        for(auto ptrThread:m_threads)
        {
            int threadPendNum = ptrThread->GetPendingsNum();
            if(minPendingNum < 0)
            {
                minPendingNum = threadPendNum;
                minPendingNumThread = ptrThread;
            }
            else if(threadPendNum < minPendingNum)
            {
                minPendingNum = threadPendNum;
                minPendingNumThread = ptrThread;
            }

            if(!ptrThread->IsStoped() && ThreadState::Running != ptrThread->GetState())
            {
                ptrPoolThread = ptrThread;
                break;
            }
        }        

        if(!ptrPoolThread)
        {
            if(m_threads.size() < m_maxThreadsNum)
            {
                ptrPoolThread = std::make_shared<CPoolthread> ();
                m_threads.emplace_back(ptrPoolThread);
            }
            else
            {
                pending = true;
                ptrPoolThread = minPendingNumThread;
            }
        }

        return ptrPoolThread;        
    }

    int GetThreadsNum()
    {
         std::unique_lock<mutex> lock(m_threadsMtx);
         return m_threads.size();
    }

    int GetIdelNum()
    {
         std::unique_lock<mutex> lock(m_threadsMtx);
         int idelNum=0;
         for(auto ptrThread:m_threads)
         {
            if(ptrThread->GetState() != ThreadState::Running)
            {
                ++idelNum; 
            }
         }
         return idelNum;
    }

    void ReleasIdelThreads()
    {
        m_age = 0;
        for(atuo it = m_threads.begin();it != m_threads.end();)
        {
            if(m_threads.size() <= minThreadsNum)
            {
                break;
            }

            if((*it)->IdelKeepTime() > m_idelTimepout)
            {
                (*it)->Stop();
                (*it)->Join();
                m_threads.erase(it);

            }
            else
            {
                it ++;
            }
        }
    }

private:
    std::atomic<int> m_maxThreadsNum;
    std::atomic<int> m_minThreadsNum;
    std::vector<std::shared_ptr<CPoolthread>> m_threads;
    //std::vector<std::function<void()> fun> m_task;
    std::mutex m_threadsMtx;
    //std::mutex m_taskMtx;
    int m_idelTimepout;
    int m_age;
    int m_maxAge;



};